#include <stdio.h>
#include <curl/curl.h>
#include <string>
#include <iostream>
#include <sstream>
#include<chrono>
#include <pthread.h>

#define NUM_THREADS 20

class curl_alive
{
private:
    CURL *curl;
    std::string http_path;

public:
    curl_alive();
    curl_alive(std::string url);
    ~curl_alive();
    void curl_create();
    void curl_set_url(std::string url);
    int curl_get();
    int curl_get(std::string &url);
    void curl_close();
};
static size_t push_string(void* buffer, size_t size, size_t nmemb, void* stream);
curl_alive::curl_alive()
{
    curl_create();
}

curl_alive::curl_alive(std::string url) : http_path(url)
{
    curl_create();
}

curl_alive::~curl_alive()
{
    curl_close();
}

static size_t push_string(void* buffer, size_t size, size_t nmemb, void* stream)
{
    std::string data((const char*)buffer, (size_t) size * nmemb);
    *((std::stringstream*) stream) << data << std::endl;
    return size*nmemb;
}

void curl_alive::curl_create() {
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, push_string);
    /* enable TCP keep-alive for this transfer */
    curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
    /* keep-alive idle time to 120 seconds */
    curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 300L);
    /* interval time between keep-alive probes: 60 seconds */
    curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 20L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30);

    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 20);
}

void curl_alive::curl_set_url(std::string url) {
    http_path = url;
}

void curl_alive::curl_close() {
    curl_easy_cleanup(curl);
    curl_global_cleanup();
}

int curl_alive::curl_get() {
    std::stringstream res_str;

    auto st = std::chrono::steady_clock::now();

    if (curl && http_path.length())
    {
        curl_easy_setopt(curl, CURLOPT_URL, http_path.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res_str);

        /* Perform the request, res will get the return code */
        CURLcode res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            return -1;
        }
    }
    auto ed = std::chrono::steady_clock::now();
    std::chrono::duration<double> dur = ed - st;
    std::cout << dur.count() << "--" << res_str.str();
    return 0;
}

int curl_alive::curl_get(std::string &url) {
    std::stringstream res_str;

    auto st = std::chrono::steady_clock::now();

    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &res_str);

        /* Perform the request, res will get the return code */
        CURLcode res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
            return -1;
        }
    }
    auto ed = std::chrono::steady_clock::now();
    std::chrono::duration<double> dur = ed - st;
    std::cout << dur.count() << "--" << res_str.str();
    return 0;
}

void *do_op(void *arg) {
    curl_alive *curl_obj;
    curl_obj = (curl_alive *) arg;
    for (size_t i = 0; i < 1000 / NUM_THREADS; i++) {
        curl_obj->curl_get();
    }
    return 0;
}

int main(int argc, char **argv)
{
    auto ss = std::chrono::steady_clock::now();
    std::string http_path;

    if (argc < 2)
    {
        printf("No url, using default url: http://10.10.9.18:8080/get?ID=object_0\n");
        http_path = "http://10.10.9.18:8080/get?ID=object_0";
    }
    else
    {
        printf("Get url: %s\n", argv[1]);
        http_path = argv[1];
    }

    // pthread_t tids[NUM_THREADS];
    // curl_alive **curl_objs = new curl_alive *[NUM_THREADS];

    // for (size_t i = 0; i < NUM_THREADS; i++)
    // {
    //     curl_objs[i] = new curl_alive(http_path);
    //     int ret = pthread_create(&tids[i], NULL, do_op, curl_objs[i]);
    //     if (ret != 0)
    //         std::cout << "pthread_create error: error_code=" << ret << std::endl;
    // }
    // // pthread_exit(NULL);
    // for (size_t i = 0; i < NUM_THREADS; i++)
    // {
    //     pthread_join(tids[i], NULL);
    // }

    curl_alive *curl_objs = new curl_alive();
    for (size_t i = 0; i < 1000; i++)
    {
        curl_objs->curl_get(http_path);
    }

    auto ee = std::chrono::steady_clock::now();
    std::chrono::duration<double> dur = ee - ss;
    std::cout << dur.count() << std::endl;
    
    return 0;
}